# Netty 快速开始

## 1. 概述

一个使用 Netty 的 Java 快速开始程序

## 2. 依赖导入

```gradle
implementation("io.netty:netty-all:4.2.6.Final")
```

## 3. DISCARD

世界上最简单的协议不是“Hello, World！”而是 DISCARD。这是一个丢弃任何接收到的数据而不进行任何响应的协议。

要实现 DISCARD 协议，您需要做的就是忽略所有接收到的数据。让我们直接从处理程序实现开始，该实现处理 Netty 生成的 I/O 事件。

**丢弃客户端的数据**

```java
# https://gitee.com/leo-blaze/examples/blob/master/java-stack/netty-quickstart/src/main/java/com/houguanghui/examples/netty/quickstart/DiscardServerHandler.java
public class DiscardServerHandler extends ChannelInboundHandlerAdapter { // (1)

  @Override
  public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
    // Discard the received data silently.
    ((ByteBuf) msg).release(); // (3)
  }

  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
    // Close the connection when an exception is raised.
    cause.printStackTrace();
    ctx.close();
  }
}
```

**创建一个 Netty Server 的包装类，接收端口号和处理器，代码如下**

```java
# https://gitee.com/leo-blaze/examples/blob/master/java-stack/netty-quickstart/src/main/java/com/houguanghui/examples/netty/quickstart/NettyServer.java
public class NettyServer {
  private int port;
  private ChannelHandler handler;

  public NettyServer(int port, ChannelHandler handler) {
    this.port = port;
    this.handler = handler;
  }

  public void run() throws Exception {
    IoEventLoopGroup bossGroup = new MultiThreadIoEventLoopGroup(1, NioIoHandler.newFactory());
    IoEventLoopGroup workerGroup = new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory());

    try {
      ServerBootstrap b = new ServerBootstrap(); // (2)
      b.group(bossGroup, workerGroup)
          .channel(NioServerSocketChannel.class) // (3)
          .childHandler(new ChannelInitializer<SocketChannel>() { // (4)
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
              ch.pipeline().addLast(handler);
            }
          })
          .option(ChannelOption.SO_BACKLOG, 128) // (5)
          .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)

      ChannelFuture f = b.bind(port).sync(); // (7)

      f.channel().closeFuture().sync();
    } finally {
      workerGroup.shutdownGracefully();
      bossGroup.shutdownGracefully();
    }
  }
}
```

**启动 Neety Server**

```java
final NettyServer server = new NettyServer(8080, new DiscardServerHandler());
try {
    server.run();
} catch (final Exception e) {
    e.printStackTrace();
}
```

你可以通过`telnet localhost 8080`连接到你的 Netty Server，不过这什么都不会发生，因为你的服务器并没有对客户端的数据进行处理

## 4. Hello World

接下来我们编写一个 Hello World 程序，包括一个 Netty 客户端程序和一个 Netty 服务器端程序，客户端连接到 Netty 服务器端程序时，服务端将客户端程序打印

### 我们先创建一个向客户端发送 Hello World 的处理器

```java
# https://gitee.com/leo-blaze/examples/blob/master/java-stack/netty-quickstart/src/main/java/com/houguanghui/examples/netty/quickstart/HelloWordServerHandler.java
public class HelloWordServerHandler extends ChannelInboundHandlerAdapter {

  @Override
  public void channelActive(final ChannelHandlerContext ctx) {
    final String message = "Hello World\n";
    final ByteBuf buffer = ctx.alloc().buffer();
    buffer.writeBytes(message.getBytes(StandardCharsets.UTF_8));

    final ChannelFuture f = ctx.writeAndFlush(buffer);
    f.addListener(ChannelFutureListener.CLOSE);
  }

  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    cause.printStackTrace();
    ctx.close();
  }
}
```

### 我们再创建 Netty 客户端的封装，用于启动 Netty 客户端程序

```java
# https://gitee.com/leo-blaze/examples/blob/master/java-stack/netty-quickstart/src/main/java/com/houguanghui/examples/netty/quickstart/NettyClient.java
public class NettyClient {
  private final String host;
  private final int port;
  private ChannelHandler handler;

  public NettyClient(final String host, final int port, ChannelHandler handler) {
    this.host = host;
    this.port = port;
    this.handler = handler;
  }

  public void run() throws Exception {
    final IoEventLoopGroup workerGroup = new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory());

    try {
      final Bootstrap b = new Bootstrap(); // (1)
      b.group(workerGroup); // (2)
      b.channel(NioSocketChannel.class); // (3)
      b.option(ChannelOption.SO_KEEPALIVE, true); // (4)
      b.handler(new ChannelInitializer<SocketChannel>() {
        @Override
        public void initChannel(final SocketChannel ch) throws Exception {
          ch.pipeline().addLast(handler);
        }
      });

      // Start the client.
      final ChannelFuture f = b.connect(host, port).sync(); // (5)

      // Wait until the connection is closed.
      f.channel().closeFuture().sync();
    } finally {
      workerGroup.shutdownGracefully();
    }
  }
}
```

### 我们再创建 Netty 客户端处理程序，用于接收 Netty 服务器发送的 Hello World 并打印

```java
# https://gitee.com/leo-blaze/examples/blob/master/java-stack/netty-quickstart/src/main/java/com/houguanghui/examples/netty/quickstart/HelloWordClientHandler.java
public class HelloWordClientHandler extends ChannelInboundHandlerAdapter {
  @Override
  public void channelRead(ChannelHandlerContext ctx, Object msg) {
    // 将接收到的 ByteBuf 转换为字符串
    ByteBuf buffer = (ByteBuf) msg;
    try {
      String message = buffer.toString(StandardCharsets.UTF_8);
      System.out.println("接收到服务器消息: " + message);
    } finally {
      buffer.release(); // 重要：释放缓冲区
    }
  }

  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    cause.printStackTrace();
    ctx.close();
  }
}
}
```

### 运行 Netty 服务器端 HelloWorldServer.java

```java
#https://gitee.com/leo-blaze/examples/blob/master/java-stack/netty-quickstart/src/main/java/com/houguanghui/examples/netty/quickstart/HelloWorldServer.java
public class HelloWorldServer {
  public static void main(String[] args) {
    final int port = 8080;
    final NettyServer server = new NettyServer(port, new HelloWordServerHandler());
    try {
      server.run();
    } catch (final Exception e) {
      e.printStackTrace();
    }
  }
}
```

### 运行 Netty 客户端程序 HelloWorldClient.java

```java
# https://gitee.com/leo-blaze/examples/blob/master/java-stack/netty-quickstart/src/main/java/com/houguanghui/examples/netty/quickstart/HelloWorldClient.java
public class HelloWorldClient {
  public static void main(String[] args) {
    final NettyClient client = new NettyClient("localhost", 8080, new HelloWordClientHandler());
    try {
      client.run();
    } catch (final Exception e) {
      e.printStackTrace();
    }
  }
}
```
